/*
 * Created by Wildish on 2016-10-31
 *
 * Contact me with keithknight@qq.com
 *
 * Copyright (c) 2016 Wildish
 */

package com.wildish.hybridframework.utils;

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.util.Log;

import com.wildish.hybridframework.core.HybridGlobal;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class Logger {
	public static final int Debug = 1;
	public static final int Info = 2;
	public static final int Exception = 3;
	public static final int Fatal = 4;

	private int minLevel = 3;

	private int maxLevel = 5;
	private String rootPath = "";

    private StringBuffer sb;

	public Logger(String rootPath) {
		this.rootPath = rootPath + File.separator + "logs";
		File file = new File(this.rootPath);
		if(!file.exists()) {
			file.mkdirs();
			File createNoMedia = new File(file, ".nomedia");
			try {
				createNoMedia.createNewFile();
			} catch (Exception e) {
			}
		}
        sb = new StringBuffer();
	}

	public void setTarget(int minLevel, int maxLevel) {
		this.minLevel = minLevel;
		this.maxLevel = maxLevel;
	}

	private void doCheck(String logfile) {
		File file = new File(logfile);
		if (!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@SuppressLint("SimpleDateFormat")
	private static SimpleDateFormat DateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	@SuppressLint("DefaultLocale")
	private String getLogFileName() {
		Calendar calendar = Calendar.getInstance();
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH) + 1;
		int date = calendar.get(Calendar.DATE);
		return String.format("%s/log%02d%02d%02d.txt", rootPath, year, month, date);
	}

	private void doLog(String msg, int level) {
		try {
			Date tNow = new Date();
			String logfile = getLogFileName();
			this.doCheck(logfile);
			//
			String prev = DateFormat.format(tNow);
			switch (level) {
			case Debug:
				prev += " D ";
                Log.d("Logger", msg);
				break;
			case Info:
				prev += " I ";
                Log.i("Logger", msg);
				break;
			case Exception:
				prev += " E ";
                Log.e("Logger", msg);
				break;
			case Fatal:
				prev += " F ";
                Log.e("Logger", msg);
				break;
			default:
                Log.d("Logger", msg);
				prev += " U ";
				break;
			}
			// check level
			if(level >= this.minLevel && level <= this.maxLevel) {
				String enter = "\r\n";

                sb.append(enter)
                    .append(prev)
                    .append(msg);
                if(sb.length() > 1024 * 10) { // 10kb
                    flush();
                }
			}
		} catch (java.lang.Exception e) {
			e.printStackTrace();
        }
	}

	public void log(String msg, int level) {
        this.doLog(msg, level);
	}

	public void log(String msg) {
        this.doLog(msg, Debug);
	}

	public void log(java.lang.Exception e) {
		log(e, Exception);
	}

	public void log(java.lang.Exception e, int level) {
		StringBuilder sb = new StringBuilder();
		sb.append(e.getClass().getName()).append(":").append(e.getMessage())
				.append("\r\n");
		StackTraceElement[] stack = e.getStackTrace();
		for (StackTraceElement err : stack) {
			sb.append("\t").append(err.getClassName()).append(".")
					.append(err.getMethodName()).append(" line:")
					.append(err.getLineNumber()).append("\r\n");
		}
		this.log(sb.toString(), level);
	}

    public synchronized void flush() {
        try{
            String logfile = getLogFileName();
            this.doCheck(logfile);
            FileOutputStream fs = new FileOutputStream(logfile, true);
            fs.write(sb.toString().getBytes("UTF-8"));
            fs.close();
            sb.setLength(0);
        } catch(Exception e) {
            Log.e("Logger", e.getMessage());
        }
    }

    public void flushAndExit() {
        AsyncTask<Void, Integer, Boolean> task = new AsyncTask<Void, Integer, Boolean>() {
            @Override
            protected Boolean doInBackground(Void... params) {
                flush();
                return true;
            }

            @Override
            protected void onPostExecute(Boolean result) {
                if(result) {
                    System.exit(0);
                }
            }
        };
        task.execute();
    }
}
